<?php

namespace app\service;

use Aws\Exception\AwsException;
use Aws\S3\S3Client;
use Exception;
use think\facade\Log;

class MinioService extends BaseService
{
    protected string $host;
    protected int $port;
    protected string $userName;
    protected string $password;
    protected S3Client $minio;

    /**
     * @throws Exception
     */
    public function initialize()
    {
        $this->host = config("minio.host");
        $this->port = config("minio.port");
        $this->userName = config("minio.user_name");
        $this->password = config("minio.password");
        $this->connect();
    }

    /**
     * 连接minio
     * @throws Exception
     */
    protected function connect() {
        $this->minio = new S3Client([
            "version"   => "latest",
            'region'  => 'ap-east-1',
            'endpoint' => $this->host . ":" . $this->port,
            'use_path_style_endpoint' => true,
            'credentials' => [
                'key'    => $this->userName,
                'secret' => $this->password,
            ],
        ]);
        try {
            $this->minio->listBuckets();
        } catch (AWSException $e){
            Log::error(date("Y-m-d H:i:s"). " Minio服务器连接失败 ".$e->getMessage());
            throw new Exception("Minio服务器连接失败");
        }
    }

    /**
     * 获取桶列表
     * @return mixed
     * @throws Exception
     */
    public function getBucketsList(): mixed
    {
        try {
            $bucketList = $this->minio->listBuckets()->toArray()["Buckets"];
        }catch (AWSException $e) {
            throw new Exception("获取桶失败 ".$e->getMessage());
        }
        return $bucketList;
    }

    /**
     * 创建新的桶
     * @throw AWSException
     * @param string $bucketName
     * @return bool|string
     * @throws Exception
     */
    public function createBucket(string $bucketName): bool|string
    {
        try {
            $result = $this->minio->createBucket([
                "CreateBucketConfiguration" => [
                    "LocationConstraint" => "ap-east-1"
                ],
                "Bucket"    => $bucketName
            ]);
            if(isset($result->toArray()["Location"])) {
                return $result->toArray()["Location"];
            }
        }catch (AWSException $e) {
            $info = $this->getCodeTag($e->getMessage());
            if("BucketAlreadyOwnedByYou" === $info["value"]) {
                Log::error(date("Y-m-d H:i:s")." 桶{$bucketName}创建失败 详情 您尝试创建的桶已经存在,并且您拥有它");
                throw new Exception("桶{$bucketName}创建失败 详情 您尝试创建的桶已经存在,并且您拥有它");
            }
        }
        return false;
    }

    /**
     * 获取minio中的文件列表
     * @param string $path
     * @return bool|array
     */
    public function getFileList(string $path): bool|array
    {
        $fileList = $this->minio->listObjects([
            "Bucket"    => "xml",
            "Prefix"    => $path
        ])->toArray();
        if(array_key_exists("Contents",$fileList)) {
            $fileList = array_column($fileList["Contents"],"Key");
        }else {
            $fileList = [];
        }
        return $fileList;
    }

    /**
     * 上传文件
     * @param string $key
     * @param string $path
     * @return mixed
     */
    public function putFile(string $key, string $path): mixed
    {
        $result = $this->minio->putObject([
            "Bucket"   => "xml",
            "Key"       => $key,
            "Body"      => fopen($path,"r")
        ]);

        return $result->toArray()["ObjectURL"];
    }

    /**
     * @param string $key
     * @return string
     */
    public function getFileUri(string $key): string
    {
        $command = $this->minio->getCommand('GetObject', [
            "Bucket"   => "xml",
            "Key"       => $key
        ]);
        $presignedRequest = $this->minio->createPresignedRequest($command, '+10 minutes');
        return (string)$presignedRequest->getUri();
    }

    /**
     * @param $msg
     * @return mixed
     */
    protected function getCodeTag($msg): mixed
    {
        $xml = strstr($msg,"<?");
        $p = xml_parser_create();
        $info = [];
        xml_parse_into_struct($p , $xml, $info, $index);
        foreach ($info as $value) {
            if("CODE" === $value["tag"]) {
                return $value;
            }
        }
        return false;
    }
}